package com.heima.wemedia.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.apis.article.IArticleClient;
import com.heima.common.constants.KafkaConstans;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsDownOrUpDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmNewsTaskService;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.helper.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Xiao
 * @version 1.0
 * @description
 * @date 2021/11/22
 */
@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    private static final Integer RESET_PAGE = 1;
    @Autowired
    private WmNewsMaterialMapper newsMaterialMapper;
    @Autowired
    private WmMaterialMapper materialMapper;
    @Autowired
    private WmNewsAutoScanService autoScanService;
    /**
     * 自媒体文章列表查询
     * @param dto
     * @return
     */
    @Override
    public ResponseResult getNews(WmNewsPageReqDto dto) {
        if (ObjectUtil.isEmpty(dto)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        dto.checkParam();
        if (ObjectUtil.isNotEmpty(dto.getResetPage())) {
            dto.setPage(dto.getResetPage() ? RESET_PAGE : dto.getPage());
        }
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUserId());
        Date beginPubDate = dto.getBeginPubDate();
        Date endPubDate = dto.getEndPubDate();
        if (ObjectUtil.isNotEmpty(beginPubDate) && ObjectUtil.isNotEmpty(endPubDate)) {
            wrapper.between(WmNews::getCreatedTime, beginPubDate, endPubDate);
        }
        if (ObjectUtil.isNotEmpty(dto.getChannelId())) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }
        if (ObjectUtil.isNotEmpty(dto.getStatus())) {
            wrapper.eq(WmNews::getStatus, dto.getStatus());
        }
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }
        wrapper.orderByDesc(WmNews::getCreatedTime);
        this.page(page, wrapper);
        ResponseResult responseResult = new PageResponseResult(
                dto.getPage(), dto.getSize(), (int)page.getTotal());
        responseResult.setData(page.getRecords());
        return responseResult;
    }

    @Autowired
    private IArticleClient articleClient;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    /**
     * 自媒体文章上架/下架
     * @param dto
     * @return
     */
    @Override
//    @GlobalTransactional
    public ResponseResult downOrUp(WmNewsDto dto) {
        //参数校验
        if (ObjectUtil.isEmpty(dto)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //校验文章是否存在
        WmNews wmNews = this.getById(dto.getId());
        if (ObjectUtil.isEmpty(wmNews)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //校验是否为发布状态
        if (!WemediaConstants.WM_NEWS_STATUS_SCAN_PUBLISH.equals(wmNews.getStatus())) {
            return ResponseResult.errorResult(
                    AppHttpCodeEnum.PARAM_INVALID, "文章不是发布状态");
        }
        if (ObjectUtil.isEmpty(wmNews)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        ApArticleConfig articleConfig = new ApArticleConfig();
        articleConfig.setArticleId(wmNews.getArticleId());
        if (WemediaConstants.WM_NEWS_ENABLE_TRUE.equals(dto.getEnable())) {
            articleConfig.setIsDown(WemediaConstants.WM_NEWS_UP);
        }
        if (WemediaConstants.WM_NEWS_ENABLE_FALSE.equals(dto.getEnable())) {
            articleConfig.setIsDown(WemediaConstants.WM_NEWS_DOWN);
        }
        if (ObjectUtil.isNotEmpty(wmNews.getArticleId())) {
            /*ResponseResult result = articleClient.delOrDownArticle(articleConfig);
            if (AppHttpCodeEnum.SUCCESS.getCode() != result.getCode()) {
                throw  new CustomException(AppHttpCodeEnum.SERVER_ERROR);
            }*/
            kafkaTemplate.send(
                    KafkaConstans.WM_NEWS_DEL_OR_DOWN_TOPIC, JSON.toJSONString(articleConfig));
        }
        //模拟异常，测试分布式事务
        //System.err.println(1/0);
        /*wmNews.setId(dto.getId());
        wmNews.setEnable(dto.getEnable());
        this.updateById(wmNews);*/
        Integer enable = dto.getEnable();
        if (ObjectUtil.isNotEmpty(enable) &&
                (WemediaConstants.WM_NEWS_ENABLE_FALSE.equals(enable) ||
                WemediaConstants.WM_NEWS_ENABLE_TRUE.equals(enable))) {
            this.update(Wrappers.<WmNews>lambdaUpdate()
                    .set(WmNews::getEnable, enable)
                    .eq(WmNews::getId, dto.getId()));
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 自媒体文章删除
     * @param id
     * @return
     */
    @Override
//    @GlobalTransactional
    public ResponseResult delNews(Integer id) {
        if (ObjectUtil.isEmpty(id)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        WmNews wmNews = this.getById(id);

        ApArticleConfig articleConfig = new ApArticleConfig();
        articleConfig.setArticleId(wmNews.getArticleId());
        articleConfig.setIsDelete(WemediaConstants.WM_NEWS_DELETE);
        if (ObjectUtil.isNotEmpty(wmNews.getArticleId())) {
            /*ResponseResult result = articleClient.delOrDownArticle(articleConfig);
            if (AppHttpCodeEnum.SERVER_ERROR.getCode() == result.getCode()) {
                throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
            }*/
            kafkaTemplate.send(
                    KafkaConstans.WM_NEWS_DEL_OR_DOWN_TOPIC, JSON.toJSONString(articleConfig));
        }
        this.removeById(id);
        /*LambdaQueryWrapper<WmNewsMaterial> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WmNewsMaterial::getNewsId, id);
        newsMaterialMapper.delete(wrapper);*/
        newsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                .eq(WmNewsMaterial::getNewsId, id));

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 查看文章详情
     * @param id
     * @return
     */
    @Override
    public ResponseResult editNews(Integer id) {
        if (ObjectUtil.isEmpty(id)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        WmNews wmNews = this.getById(id);
        if (ObjectUtil.isEmpty(wmNews)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        return ResponseResult.okResult(wmNews);
    }

    @Autowired
    private WmNewsTaskService wmNewsTaskService;
    /**
     * 添加/修改文章
     * @param dto
     * @return
     */
    @Override
//    @GlobalTransactional
    public ResponseResult submitNews(WmNewsDto dto) {
        //1.校验参数
        if (ObjectUtil.isEmpty(dto) || StringUtils.isBlank(dto.getContent())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.保存或修改文章
        //拷贝属性
        WmNews wmNews = new WmNews();
        BeanUtil.copyProperties(dto, wmNews);
        if (CollUtil.isNotEmpty(dto.getImages())) {
            String images = StringUtil.join(dto.getImages(), ",");
            wmNews.setImages(images);
        } else {
            wmNews.setImages(WemediaConstants.WM_NEWS_IMAGES_NONE);
        }
        if (ObjectUtil.isEmpty(dto.getPublishTime())) {
            wmNews.setPublishTime(new Date());
        }
        //如果封面类型为自动(-1)
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            wmNews.setType(null);
        }
        saveOrUpdateWmNews(wmNews);
        //3.是草稿 --> 结束
        if (WemediaConstants.WM_NEWS_STATUS_NORMAL.equals(dto.getStatus())) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        //4.不是草稿 --> 保存文章内容图片与素材的关系
        List<String> contentImages = ectractUrlInfo(dto.getContent());
        saveRelativeInfo(contentImages, wmNews.getId(), WemediaConstants.WM_CONTENT_REFERENCE);
        //5.不是草稿 --> 保存文章封面图片与素材的关系
        saveRelativeInfoForCover(dto, wmNews, contentImages, WemediaConstants.WM_COVER_REFERENCE);
        //文章审核、发布
//        autoScanService.autoScanWmNews(wmNews.getId());
        wmNewsTaskService.addNewsToTask(wmNews.getId(), wmNews.getPublishTime());
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 保存文章封面图片与素材的关系
     * 第一个功能：如果当前封面类型为自动，则设置封面类型的数据
     *      匹配规则：
     *      1，如果内容图片大于等于1，小于3  单图  type 1
     *      2，如果内容图片大于等于3  多图  type 3
     *      3，如果内容没有图片，无图  type 0
     * 第二个功能：保存封面图片与素材的关系
     * @param dto
     * @param wmNews
     * @param contentImages
     * @param type
     */
    private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews,
                                          List<String> contentImages, Integer type) {
        List<String> coverImages = dto.getImages();
        //一、如果封面类型设置为自动，则根据内容图片的数量自动选择封面类型
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            if (contentImages.size() >= WemediaConstants.WM_NEWS_MANY_IMAGE) {
                //如果内容图片大于等于3  多图  type 3
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                coverImages = contentImages.stream().limit(3L).collect(Collectors.toList());
            } else if (contentImages.size() >= WemediaConstants.WM_NEWS_SINGLE_IMAGE) {
                //如果内容图片大于等于1，小于3  单图  type 1
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                coverImages = contentImages.stream().limit(1L).collect(Collectors.toList());
            } else {
                //如果内容没有图片，无图  type 0
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            //设置封面图片
            if (CollUtil.isNotEmpty(coverImages)) {
                wmNews.setImages(StringUtil.join(coverImages, ","));
            }
            //修改文章(type/images)
            this.updateById(wmNews);
        }
        //二、保存封面图片与素材的关系
        if (ObjectUtil.isNotEmpty(coverImages)) {
            saveRelativeInfo(coverImages, wmNews.getId(), type);
        }
    }

    /**
     * 保存文章内容/封面与图片的关系
     * @param images
     * @param newsId
     */
    private void saveRelativeInfo(List<String> images, Integer newsId, Integer type) {
        if (CollUtil.isNotEmpty(images)) {
            //根据路径查找图片
            LambdaQueryWrapper<WmMaterial> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(WmMaterial::getUrl, images);
            List<WmMaterial> materials = materialMapper.selectList(wrapper);
            //判断图片是否有效
            if (CollUtil.isEmpty(materials) || images.size() != materials.size()) {
                throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            //提取materialId
            List<Integer> materialIds = materials.stream()
                    .map(WmMaterial::getId)
                    .collect(Collectors.toList());
            newsMaterialMapper.saveRelations(materialIds, newsId, type);
        }
    }

    /**
     * 提取文章内容中的图片
     * @param content
     * @return
     */
    private List<String> ectractUrlInfo(String content) {
        List<Map> maps = JSON.parseArray(content, Map.class);
        List<String> contentImages = new ArrayList<>();
        for (Map map : maps) {
            if (WemediaConstants.WM_NEWS_TYPE_IMAGE
                    .equals(map.get(WemediaConstants.WM_NEWS_TYPE))) {
                contentImages.add(map.get(WemediaConstants.WM_NEWS_VALUE).toString());
            }
        }
        return contentImages;
    }

    /**
     * 添加/修改新闻
     * @param wmNews
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {
        wmNews.setUserId(WmThreadLocalUtil.getUserId());
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable(1);
        if (ObjectUtil.isEmpty(wmNews.getId())) {
            this.save(wmNews);
        } else {
            LambdaQueryWrapper<WmNewsMaterial> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(WmNewsMaterial::getNewsId, wmNews.getId());
            newsMaterialMapper.delete(wrapper);
            this.updateById(wmNews);
        }
    }
}
